/*
 * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "usb_ext_screen.h"
#include "esp_timer.h"
#include "usb_frame.h"
#include "usbd_core.h"
#include "esp_heap_caps.h"
#include "lcd.h"
#include "usb_aux_screen_ui.h"
#define CONFIG_VENDOR_TASK_PRIORITY 10
static const char *TAG = "app_vendor"; // 日志标签
static frame_t *current_frame = NULL;  // 当前正在处理的帧指针
//
static TaskHandle_t transfer_task_handle = NULL; // 传输任务句柄
static uint8_t *usb_rx_buf = NULL;
//--------------------------------------------------------------------+
// Vendor callbacks
//--------------------------------------------------------------------+

#define CONFIG_USB_HS                0
#define VENDOR_BUF_SIZE              CONFIG_USB_HS ? 512 : 64

// -- 显示数据包类型定义
#define UDISP_TYPE_RGB565 0 // RGB565格式
#define UDISP_TYPE_RGB888 1 // RGB888格式
#define UDISP_TYPE_YUV420 2 // YUV420格式
#define UDISP_TYPE_JPG 3    // JPEG格式

// 显示帧头结构定义（紧凑排列，无字节对齐填充）
typedef struct
{
    uint16_t crc16;              // CRC16校验值
    uint8_t type;                // 帧类型（RGB565/RGB888/YUV420/JPG）
    uint8_t cmd;                 // 命令字
    uint16_t x;                  // 显示起始坐标X
    uint16_t y;                  // 显示起始坐标Y
    uint16_t width;              // 帧宽度
    uint16_t height;             // 帧高度
    uint32_t frame_id : 10;      // 帧ID（10位）
    uint32_t payload_total : 22; // 有效载荷总长度（22位）// 填充至32位对齐
} __attribute__((packed)) udisp_frame_header_t;

/**
 * @brief 传输任务，从帧缓冲区获取已填充的帧并发送到LCD显示
 * @param pvParameter 任务参数（未使用）
 */
void transfer_task(void *pvParameter)
{
    // 分配3个帧缓冲区，每个大小为JPEG_BUFFER_SIZE
    frame_allocate(JPEG_BUFFER_SIZE);
    frame_t *usr_frame = NULL;

    while (1)
    {
        // 获取已填充的帧
        usr_frame = frame_get_filled();
       //ESP_LOGI(TAG, "width:%d, height:%d, total:%d", usr_frame->info.width, usr_frame->info.height, usr_frame->info.total);
        // 将帧数据发送到LCD显示
        refresh_usb_data2screen(usr_frame->data, usr_frame->info.width, usr_frame->info.height, usr_frame->info.total);
        //app_lcd_draw(usr_frame->data, usr_frame->info.total, usr_frame->info.width, usr_frame->info.height);
        // 将空帧返回给缓冲区
        frame_return_empty(usr_frame);
    }
}

/**
 * @brief 跳过指定长度的数据（用于处理无法处理的帧）
 * @param frame_info 帧信息结构体指针
 * @param len 要跳过的数据长度
 * @return true-跳过完成，帧数据处理结束；false-还需要继续跳过
 */
static bool buffer_skip(frame_info_t *frame_info, uint32_t len)
{
    // 检查已接收数据加上本次跳过长度是否达到总长度
    if (frame_info->received + len >= frame_info->total)
    {
        return true;
    }
    // 更新已接收数据长度
    frame_info->received += len;
    return false;
}

/**
 * @brief 填充帧数据到缓冲区
 * @param frame 帧结构体指针
 * @param buf 数据缓冲区指针
 * @param len 数据长度
 * @return true-帧数据填充完成；false-帧数据未完成
 */
static bool buffer_fill(frame_t *frame, uint8_t *buf, uint32_t len)
{
    if (0 == len)
    {
        return false;
    }

    // 添加数据到帧，如果失败则不处理（可能有日志输出）
    if (frame_add_data(frame, buf, len) != ESP_OK)
    {
    }
    // 更新已接收数据长度
    frame->info.received += len;

    // 检查是否已接收完整帧数据
    if (frame->info.received >= frame->info.total)
    {
        // 发送已填充的帧到处理队列
        frame_send_filled(frame);
        return true;
    }
    return false;
}
void usb_ext_screen_out_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    if (usb_rx_buf)
    {
        static bool skip_frame = false;              // 跳过帧标志
        static frame_info_t skip_frame_info = { 0 }; // 跳帧信息记录
        if (nbytes > 0) {
            // 情况1：没有当前帧且不需要跳帧 - 处理新帧头
            if (!current_frame && !skip_frame) {
                udisp_frame_header_t *pblt = (udisp_frame_header_t *)usb_rx_buf;
               // ESP_EARLY_LOGI(TAG, "cmd: %d, type: %d, x: %d, y: %d, w: %d, h: %d, total: %d", pblt->cmd, pblt->type ,pblt->x, pblt->y, pblt->width, pblt->height, pblt->payload_total);
                switch (pblt->type) {
                    case UDISP_TYPE_RGB565:
                    case UDISP_TYPE_RGB888:
                    case UDISP_TYPE_YUV420:
                    case UDISP_TYPE_JPG: {
                        // 检查显示坐标和分辨率是否匹配（全屏显示）
                        if (pblt->x != 0 || pblt->y != 0 ||
                            pblt->width != BSP_LCD_H_RES ||
                            pblt->height != BSP_LCD_V_RES) {
                            break;
                        }

                        // FPS计算（每50帧计算一次）
                        static int fps_count = 0;
                        static int64_t start_time = 0;
                        fps_count++;
                        if (fps_count == 50) {
                            int64_t end_time = esp_timer_get_time();
                           // ESP_EARLY_LOGI(TAG, "输入帧率: %f", 1000000.0 / ((end_time - start_time) / 50.0));
                            start_time = end_time;
                            fps_count = 0;
                        }

                        // 获取空帧缓冲区
                        current_frame = frame_get_empty();
                        if (current_frame) {
                            // 设置帧信息
                            current_frame->info.width = pblt->width;
                            current_frame->info.height = pblt->height;
                            current_frame->info.total = pblt->payload_total;
                            current_frame->info.received = 0;

                            // ESP_LOGD(TAG, "read len: %d, frame x:%d y:%d w:%d h:%d total:%" PRIu32 " (%d)",
                            //          nbytes, pblt->x, pblt->y, pblt->width, pblt->height,
                            //          current_frame->info.total, (pblt->width) * (pblt->height) * 2);

                            // 填充数据（跳过帧头）
                            if (buffer_fill(current_frame, &usb_rx_buf[sizeof(udisp_frame_header_t)],
                                            nbytes - sizeof(udisp_frame_header_t))) {
                                current_frame = NULL; // 帧已完整处理
                            }
                        } else {
                            // 无法获取空帧，设置跳帧模式
                            memset(&skip_frame_info, 0, sizeof(skip_frame_info));
                            skip_frame_info.total = pblt->payload_total;
                            skip_frame = true;
                            // 跳过已接收的数据（帧头之后的部分）
                            buffer_skip(&skip_frame_info, nbytes - sizeof(udisp_frame_header_t));
                            //ESP_EARLY_LOGE(TAG, "get empty frame failed");
                        }
                        break;
                    }
                    default:
                        ESP_EARLY_LOGE(TAG, "err command");
                        break;
                }
            }
            // 情况2：处于跳帧模式 - 跳过数据
            else if (skip_frame) {
                if (buffer_skip(&skip_frame_info, nbytes)) {
                    current_frame = NULL;
                    skip_frame = false; // 跳帧结束
                }
            }
            // 情况3：有当前帧且不需要跳帧 - 继续填充数据
            else {
                if (buffer_fill(current_frame, usb_rx_buf, nbytes)) {
                    current_frame = NULL; // 帧已完整处理
                }
            }
        }

        //继续读取数据到缓冲区
        usbd_ep_start_read(0, EPNUM_VENDOR, usb_rx_buf, VENDOR_BUF_SIZE);
    }
}
void usb_ext_screen_in_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    ESP_EARLY_LOGI(TAG, "call usb_ext_screen_in_callback");
}

/*
 *usb配置成功后回调函数   
*/
void usb_ext_screen_usbd_event_cfg(void)
{
    usb_rx_buf = (uint8_t *)malloc(VENDOR_BUF_SIZE);
    usbd_ep_start_read(0, EPNUM_VENDOR, usb_rx_buf, VENDOR_BUF_SIZE);
}
/**
 * @brief 初始化Vendor应用
 * @return ESP_OK 成功
 */
esp_err_t app_vendor_init(void)
{
    // 创建传输任务（固定在核心0运行）
    xTaskCreatePinnedToCore(transfer_task, "transfer_task", 4096, NULL,
                            CONFIG_VENDOR_TASK_PRIORITY, &transfer_task_handle, 0);
    return ESP_OK;
}
/**
 * @brief 初始化Vendor应用
 * @return ESP_OK 成功
 */
esp_err_t app_vendor_deinit(void)
{
    // 删除传输任务（固定在核心0运行）
    vTaskDelete(transfer_task_handle);
    //释放帧缓冲区
    frame_free();

    //释放USB接收缓冲区
    if (usb_rx_buf)
    {
        free(usb_rx_buf);
        usb_rx_buf = NULL;
    }
    return ESP_OK;
}